#ifndef ARRAY_H
#define ARRAY_H
#ifndef CONTAINER_H
#include "Container.h"
#define CONTAINER_H
#endif

using namespace std;
#include <string>

template <class T>
class Array_t : Container_t<T>{
public:
	Array_t() : Container_t<T>() {
		expandValue = defExpandValue;
		internalArray = new T*[expandValue];
		maxSize = expandValue;
		this->size = 0;
	}

	Array_t(Array_t<T>& other) : Container_t<T>(Container_t<T>& other){
		expandValue = other.expandValue;
		if (other.count() > 0) {
			internalArray = new T*[other.maxSize];
			this->maxSize = other.maxSize;
			this->size = other.count();
			//copy values
			for (int index = 0; index <  this->maxSize ; ++index) {
				this->internalArray[index] = other.internalArray[index];
			}
		} else {
			internalArray = new T*[expandValue];
			maxSize = expandValue;
			this->size = 0;
		}
	}
	~Array_t() {
		if(internalArray != 0x0)  {
			delete[] internalArray;
		}
		internalArray = 0x0;
	}

	T* operator[] (int index){
		return internalArray[index];
	}

	int count() {
		return this->size;
	}
	T* find(const T& compElement) const {
		int index = findIndex(compElement);
		if (index == this->size) {
			return 0;
		}
		return (internalArray[index]);
	}
	void insert(T* const element){
		//resize;
		if (this->maxSize <= this->count()){
			resizeInternalArray();
		}
		internalArray[this->count()] = element;
		this->size++;
	}
	void append(T* const element, int index){
		if(size==0) {
			insert(element);
			return;
		}
		//resize;
		if (this->maxSize <= this->count()){
			resizeInternalArray();
		}
		shiftOneRightInArray(index);
		internalArray[index+1] = element;
		this->size++;
	}
	void prepend(T* const element, int index){
		//resize;
		if (this->maxSize <= this->count()){
			resizeInternalArray();
		}
		shiftOneRightInArray(index);
		internalArray[index] = element;
		this->size++;
	}
	T* remove(const T& compElement){
		int indexToRemove = findIndex(compElement);	
		if (indexToRemove>=this->count()) {
			//element doesn't exist
			return 0x0;
		}
		T* element = internalArray[indexToRemove];
		shiftOneLeftInArray(indexToRemove);
		return element;
	}
	void removeAll() {
		if (this->internalArray != 0x0) {
			delete[] this->internalArray;
		}
		internalArray = new T*[this->expandValue];
		this->maxSize = this->expandValue;
		this->size = 0;
		return;
	}
	void removeAndDeleteAll() {
		//delete all
		for(int index = 0; index < this->size; index++) {
			if (this->internalArray[index] != 0x0) {
				delete this->internalArray[index];
			}
			internalArray[index] = 0; //redirect it to null
		}
		//removeall
		removeAll();
		return;
	}


private:
	T** internalArray;
	int maxSize;
	static const int defExpandValue = 8;
	int expandValue;

	void resizeInternalArray(){
		if(this->maxSize < this->count()){
			//throw error messege
			//should never happend
			throw std::string("Array: Internal error, array overflow");
		}
		T** internalArrayTMP = new T*[maxSize+expandValue]; 
		//iterate over the array and copy old values
		for (int index = 0; index < maxSize ; index++) {
			internalArrayTMP[index] = internalArray[index];
		}
		if(internalArray != 0x0) {
			delete[] internalArray;
		}
		internalArray = internalArrayTMP;
		maxSize += expandValue;
		return;
	}
	void shiftOneRightInArray(int index) {
		if(size < index) {
			throw std::string("Array: Illegal index");
		}
		if(maxSize <= this->size) {
			resizeInternalArray();
		}
		for(int i = this->size ;index < i; --i) {
			internalArray[i] = internalArray[i-1];
		}
		return;
	}
	void shiftOneLeftInArray(int index) {
		if(this->size <= index) {			
			throw std::string("Array: Illegal index");
		}
		if(maxSize <= this->size) {
			resizeInternalArray();
		}
		for(;index < this->size ; index++) {
			internalArray[index] = internalArray[index+1];
		}
		internalArray[this->size-1] = 0;
		size--;
		return;
	}
	int findIndex(const T& compElement) const {
		int arrayIndex = 0;
		for (; arrayIndex < this->size ; ++arrayIndex) 
		{
			if(internalArray[arrayIndex]!=0)
			{ 
				if(*internalArray[arrayIndex]==compElement) 
				{
					break;
				}
			}
		}
		return arrayIndex;
	}
	//implementing this method will be used for the assign constructor
	void copyToContainer(Container_t<T>* other) const
	{
		other->removeAll();
		for(int index = 0; index < this->size; ++index) {
			other->insert(this->internalArray[index]);
		}
	}
};

#endif //ARRAY_H